package com.chrono.stock.dao;

import java.io.Serializable;
import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.criterion.Example;

import com.chrono.stock.dao.model.EntityModel;
import com.chrono.stock.dao.util.HibernateUtil;

public class DAOImpl implements DAO {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(DAOImpl.class);

	protected Session getSession() {

		Session returnSession = HibernateUtil.getSessionFactory()
				.getCurrentSession();

		return returnSession;
	}

	protected Session openSession() {
		Session returnSession = HibernateUtil.getSessionFactory().openSession();

		return returnSession;
	}

	@Override
	public <T extends EntityModel> void persist(T transientInstance) {
		if (logger.isDebugEnabled()) {
			logger.debug("persist(T) - start");
		}
		try {
			getSession().beginTransaction();
			getSession().persist(transientInstance);
			getSession().getTransaction().commit();
		} catch (RuntimeException re) {
			logger.error("persist(T)", re);
			getSession().getTransaction().rollback();
			throw re;
		}
		if (logger.isDebugEnabled()) {
			logger.debug("persist(T) - end");
		}
	}

	@Override
	public void delete(EntityModel persistentInstance) {
		if (logger.isDebugEnabled()) {
			logger.debug("delete(ZalaEntity) - start");
		}
		try {
			getSession().beginTransaction();
			getSession().delete(persistentInstance);
			getSession().getTransaction().commit();
		} catch (RuntimeException re) {
			logger.error("delete(ZalaEntity)", re);
			getSession().getTransaction().rollback();
			throw re;
		}
		if (logger.isDebugEnabled()) {
			logger.debug("delete(ZalaEntity) - end");
		}
	}

	@Override
	public <T extends EntityModel> T merge(T transientInstance) {
		if (logger.isDebugEnabled()) {
			logger.debug("merge(T) - start");
		}
		try {
			getSession().beginTransaction();
			transientInstance = (T) getSession().merge(transientInstance);
			getSession().getTransaction().commit();
			if (logger.isDebugEnabled()) {
				logger.debug("merge(T) - end");
			}
			return transientInstance;
		} catch (RuntimeException re) {
			logger.error("merge(T)", re);
			getSession().getTransaction().rollback();
			throw re;
		}
	}

	@Override
	public <T extends EntityModel> List<T> findByExample(T instance) {
		if (logger.isDebugEnabled()) {
			logger.debug("findByExample(T) - start");
		}
		try {

			if (!getSession().getTransaction().isActive()) {
				getSession().beginTransaction();
			}
			List<T> results = getSession()
					.createCriteria(instance.getClass().getName())
					.add(Example.create(instance)).list();

			if (logger.isDebugEnabled()) {
				logger.debug("findByExample(T) - end");
			}
			return results;
		} catch (RuntimeException re) {
			logger.error("findByExample(T)", re);
			throw re;
		}
	}

	@Override
	public <T extends EntityModel> List<T> findAll(Class<T> clazz) {
		if (logger.isDebugEnabled()) {
			logger.debug("findAll(Class<T>) - start");
		}
		try {
			if (!getSession().getTransaction().isActive()) {
				getSession().beginTransaction();
			}
			List<T> list = getSession().createCriteria(clazz.getName()).list();
			if (logger.isDebugEnabled()) {
				logger.debug("findAll(Class<T>) - end");
			}
			return list;
		} catch (RuntimeException re) {
			logger.error("findAll(Class<T>)", re);

			throw re;
		}
	}

	@Override
	public <T extends EntityModel> SodaQuery<T> getQuery(Class<T> clazz) {
		if (logger.isDebugEnabled()) {
			logger.debug("getCriteria(Class<T>) - start");
		}
		SodaQuery<T> returnZalaQuery = new SodaQuery<T>(openSession(), clazz);
		if (logger.isDebugEnabled()) {
			logger.debug("getCriteria(Class<T>) - end");
		}
		return returnZalaQuery;
	}

	@Override
	public <T extends EntityModel> List<T> find(SodaQuery<T> c) {
		if (logger.isDebugEnabled()) {
			logger.debug("find(SodaQuery<T>) - start");
		}
		try {
			List<T> list = c.list();
			c.getSession().getTransaction().commit();

			if (logger.isDebugEnabled()) {
				logger.debug("find(SodaQuery<T>) - end");
			}
			return list;
		} catch (RuntimeException re) {
			logger.error("find(SodaQuery<T>)", re);
			throw re;
		} finally {
			c.getSession().close();
		}

	}

	@Override
	public <T extends EntityModel> T findById(Serializable id, Class<T> clazz) {
		if (logger.isDebugEnabled()) {
			logger.debug("findById(Integer, Class<T>) - start");
		}
		try {
			Session session = getSession();
			session.beginTransaction();
			T instance = (T) session.get(clazz.getName(), id);
			if (logger.isDebugEnabled()) {
				logger.debug("findById(Integer, Class<T>) - end");
			}
			return instance;
		} catch (RuntimeException re) {
			logger.error("findById(Integer, Class<T>)", re);
			throw re;
		}
	}

}
